#include <iostream>
#include <stdexcept>
#include <stdlib.h>
#include "parser.h"
#include "commandline.h"
#include "command.h"
#include "internal.h"
#include "pipeline.h"
#include "process.h"
#include "token.h"


Parser::Parser(std::istream * in) : in(in)
{
	token = NULL;
}

Parser::~Parser()
{
}

void Parser::check(Token::token_t type)
{
    if (token->getType() != type)
    {
		throw std::runtime_error("Syntax error");
	}
}

void Parser::accept(Token::token_t type)
{
	check(type);
    acceptIt();
}

void Parser::acceptIt()
{
	if(token != NULL)
	{
		delete token;
	}

    token = nextToken(*in);

	if (token->getType() == Token::END)
	{
		exit(0);
	}
}

CommandLine * Parser::parse()
{
    acceptIt();
    return parseCommandLine();
}

CommandLine * Parser::parseCommandLine()
{
    // parse command-line
    CommandLine * commandLine = new CommandLine();
    parseCommand(commandLine);

    // parse command-line-tail
    while (token->getType() == Token::SEQUENCE)
    {
        acceptIt();
        parseCommand(commandLine);
    }

    // end with newline
    check(Token::EOL);

    return commandLine;
}

void Parser::parseCommand(CommandLine * commandLine)
{
	if (token->getType() == Token::WORD)
	{
        string text = token->getText();

        // parse internal
        if (text == "exit" || text == "logout")
        {
            commandLine->addCommand(new Internal(Internal::EXIT));
            acceptIt();
        }
        else
        {
            // parse pipeline
            Pipeline * pipeline = parsePipeline();
            commandLine->addCommand(pipeline);
        }

    }
    else
    {
        accept(Token::WORD);
    }
}

Pipeline * Parser::parsePipeline()
{
    Pipeline * pipeline = new Pipeline();
    pipeline->addProcess(parseProcess());
    parseInput(pipeline);

    // parse pipeline-tail
    while (token->getType() == Token::PIPE)
    {
        acceptIt();
        pipeline->addProcess(parseProcess());
    }

    parseOutput(pipeline);

    return pipeline;
}

Process * Parser::parseProcess()
{
    if (token->getType() == Token::WORD)
    {
        Process * process = new Process(token->getText());
        acceptIt();

        // parse parameters
        while (token->getType() == Token::WORD)
        {
            process->addParameter(token->getText());
            acceptIt();
        }

        return process;
    }

    return NULL;
}

void Parser::parseInput(Pipeline * pipeline)
{
    if (token->getType() == Token::INPUT)
    {
        acceptIt();
        string file = token->getText();
        accept(Token::WORD);
        pipeline->setInput(file);
    }
}

void Parser::parseOutput(Pipeline * pipeline)
{
    if (token->getType() == Token::OUTPUT)
    {
       acceptIt();
       string file = token->getText();
       accept(Token::WORD);
       pipeline->setOutput(file, false);
    }
    else if (token->getType() == Token::APPEND)
    {
	   acceptIt();
       string file = token->getText();
       accept(Token::WORD);
       pipeline->setOutput(file, true);
    }
}
